import axios, { type AxiosInstance, type InternalAxiosRequestConfig, type AxiosResponse, type CancelTokenSource } from 'axios'
import { ElMessage } from 'element-plus'
import { getToken, removeToken } from '@/utils/auth'
import { useUserStore } from '@/stores/user'
import { handleRequestError } from '@/utils/errorHandler'
// import router from '@/router'
import * as NProgress from 'nprogress'

// 请求取消控制器映射
const cancelTokenMap = new Map<string, CancelTokenSource>()

// 创建axios实例
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  }
})

// 请求拦截器
service.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    NProgress.start()
    
    // 添加token
    const token = getToken()
    if (token && config.headers) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    // 添加时间戳防止缓存
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now()
      }
    }
    
    return config
  },
  (error) => {
    NProgress.done()
    // eslint-disable-next-line no-console
    console.error('请求错误:', error)
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    NProgress.done()
    
    const { code, message } = response.data
    
    // 成功响应
    if (code === 200) {
      return response.data.data
    }
    
    // 业务错误处理
    if (code === 401) {
      // 未授权，清除token并跳转登录
      removeToken()
      const userStore = useUserStore()
      userStore.logout()
      ElMessage.error(message || '登录已过期，请重新登录')
      return Promise.reject(new Error(message || '未授权'))
    }
    
    if (code === 403) {
      ElMessage.error(message || '权限不足，无法访问该资源')
      return Promise.reject(new Error(message || '权限不足'))
    }
    
    if (code === 404) {
      ElMessage.error(message || '请求的资源不存在')
      return Promise.reject(new Error(message || '资源不存在'))
    }
    
    if (code === 422) {
      ElMessage.error(message || '请求参数验证失败')
      return Promise.reject(new Error(message || '参数验证失败'))
    }
    
    if (code === 500) {
      ElMessage.error(message || '服务器内部错误，请稍后重试')
      return Promise.reject(new Error(message || '服务器内部错误'))
    }
    
    if (code === 503) {
      ElMessage.error(message || '服务暂时不可用，请稍后重试')
      return Promise.reject(new Error(message || '服务不可用'))
    }
    
    // 其他业务错误 - 不在这里显示错误消息，让调用方处理
    return Promise.reject(new Error(message || '请求失败'))
  },
  (error) => {
    NProgress.done()
    
    // 请求重试逻辑
    const config = error.config as InternalAxiosRequestConfig & { __retryCount?: number }
    if (config && typeof config.__retryCount !== 'number') {
      config.__retryCount = 0
    }
    
    if (config && (config.__retryCount || 0) < 3) {
      config.__retryCount = (config.__retryCount || 0) + 1
      
      // 只对网络错误和5xx错误进行重试
      if (!error.response || (error.response.status >= 500 && error.response.status < 600)) {
        const retryDelay = 1000
        console.warn(`请求失败，${retryDelay}ms后进行第${config.__retryCount}次重试:`, error.config?.url)
        
        return new Promise((resolve) => {
          setTimeout(() => {
            service(config).then(resolve).catch(() => {
              // 重试失败，继续正常错误处理流程
            })
          }, retryDelay)
        })
      }
    }
    
    // 使用统一的错误处理
    handleRequestError(error)
    
    let message = '网络错误'
    
    if (error.response) {
      const { status, data } = error.response
      
      switch (status) {
        case 400:
          message = (data as { message?: string })?.message || '请求参数错误'
          break
        case 401: {
          message = '未授权，请重新登录'
          removeToken()
          const userStore = useUserStore()
          userStore.logout()
          break
        }
        case 403:
          message = '权限不足，无法访问该资源'
          break
        case 404:
          message = '请求的资源不存在'
          break
        case 422:
          message = '请求参数验证失败'
          break
        case 500:
          message = '服务器内部错误，请稍后重试'
          break
        case 502:
          message = '网关错误，请稍后重试'
          break
        case 503:
          message = '服务暂时不可用，请稍后重试'
          break
        case 504:
          message = '网关超时，请稍后重试'
          break
        default:
          message = (data as { message?: string })?.message || `连接错误${status}`
      }
    } else if (error.code === 'ECONNABORTED') {
      message = '请求超时，请检查网络连接'
    } else if (error.message.includes('Network Error')) {
      message = '网络连接异常，请检查网络设置'
    } else if (error.code === 'ERR_CANCELED') {
      // 请求被取消，不显示错误消息
      return Promise.reject(error)
    }
    
    ElMessage.error(message)
    return Promise.reject(error)
  }
)

// 请求方法封装
export const request = {
  get<T = unknown>(url: string, params?: unknown, cancelKey?: string): Promise<T> {
    const config: Record<string, unknown> = { params }
    if (cancelKey) {
      const source = axios.CancelToken.source()
      cancelTokenMap.set(cancelKey, source)
      config.cancelToken = source.token
    }
    return service.get(url, config)
  },
  
  post<T = unknown>(url: string, data?: unknown, config?: Record<string, unknown> | string): Promise<T> {
    const requestConfig: Record<string, unknown> = {}
    
    // 如果第三个参数是字符串，则作为cancelKey处理
    if (typeof config === 'string') {
      const cancelKey = config
      const source = axios.CancelToken.source()
      cancelTokenMap.set(cancelKey, source)
      requestConfig.cancelToken = source.token
    } else if (config) {
      // 如果第三个参数是配置对象，则合并配置
      Object.assign(requestConfig, config)
    }
    
    return service.post(url, data, requestConfig)
  },
  
  put<T = unknown>(url: string, data?: unknown, config?: Record<string, unknown> | string): Promise<T> {
    const requestConfig: Record<string, unknown> = {}
    
    // 如果第三个参数是字符串，则作为cancelKey处理
    if (typeof config === 'string') {
      const cancelKey = config
      const source = axios.CancelToken.source()
      cancelTokenMap.set(cancelKey, source)
      requestConfig.cancelToken = source.token
    } else if (config) {
      // 如果第三个参数是配置对象，则合并配置
      Object.assign(requestConfig, config)
    }
    
    return service.put(url, data, requestConfig)
  },
  
  delete<T = unknown>(url: string, params?: unknown, cancelKey?: string): Promise<T> {
    const config: Record<string, unknown> = { params }
    if (cancelKey) {
      const source = axios.CancelToken.source()
      cancelTokenMap.set(cancelKey, source)
      config.cancelToken = source.token
    }
    return service.delete(url, config)
  },
  
  patch<T = unknown>(url: string, data?: unknown, config?: Record<string, unknown> | string): Promise<T> {
    const requestConfig: Record<string, unknown> = {}
    
    // 如果第三个参数是字符串，则作为cancelKey处理
    if (typeof config === 'string') {
      const cancelKey = config
      const source = axios.CancelToken.source()
      cancelTokenMap.set(cancelKey, source)
      requestConfig.cancelToken = source.token
    } else if (config) {
      // 如果第三个参数是配置对象，则合并配置
      Object.assign(requestConfig, config)
    }
    
    return service.patch(url, data, requestConfig)
  },
  
  upload<T = unknown>(url: string, file: File, onProgress?: (progress: number) => void): Promise<T> {
    const formData = new FormData()
    formData.append('file', file)
    
    return service.post(url, formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: (progressEvent) => {
        if (onProgress && progressEvent.total) {
          const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
          onProgress(progress)
        }
      }
    })
  },
  
  download(url: string, params?: unknown, filename?: string): Promise<void> {
    return service.get(url, {
      params,
      responseType: 'blob'
    }).then((response) => {
      const blob = new Blob([response.data])
      const downloadUrl = window.URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = downloadUrl
      link.download = filename || 'download'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
      window.URL.revokeObjectURL(downloadUrl)
    })
  },

  // 取消请求
  cancel(key: string): void {
    const source = cancelTokenMap.get(key)
    if (source) {
      source.cancel('请求被取消')
      cancelTokenMap.delete(key)
    }
  },

  // 取消所有请求
  cancelAll(): void {
    cancelTokenMap.forEach((source: CancelTokenSource) => {
      source.cancel('所有请求被取消')
    })
    cancelTokenMap.clear()
  }
}

export default service
